
\subsection{Software-Security}
 
\subsubsection{Privilege Modes}

The VexRiscv in the IOTA Crypto Core project supports the following privilege modes:

\paragraph{Machine-Mode} is the highest privilege mode, which gives code access to all functionality. By default, the CPU starts in Machine mode is then switched to lower privilege modes on boot. 

\paragraph{User Mode} is the lowest privilege mode. Code in User mode must call code running in higher privilege modes via an special interface.\\

The specification of RISC-V also mentions Supervisor- und Hypervisor-Mode but both are neither used nor implemented in the soft-cpu.


\subsubsection{Physical Memory Protection}

Although the firmware only executes "benign code", it could be compromised via external interfaces and "malicious code" could be injected or attack techniques such as buffer overflows could lead to exposing data that should not be accessible. Physical Memory Protection tries to prevent such instances but assigning privileges to blocks of memory which are checked with each access. There are three permissions which can be set: read, write, execute. 

The PMP plugin for the VexRiscv soft-cpu implements the PMP proposal from the Instruction Set Manual Volume II (Privileged Architecture) from the RISC-V Foundation\footnote{\url{https://riscv.org/specifications/privileged-isa/}} with one custom extension.

Normally, it is assumed that code running in Machine-mode has access to everything. It can be restricted by locking protected memory regions which enforces privilege checks also in machine-mode but there are some restrictions which lead to compromises.

For instance, it is not possible to give one block of memory different permissions for Machine- and User-mode. If Machine-mode is restricted (locked region), the same privileges also are applied to User-mode. In the other case (unlocked region) User-mode is restricted but everything is allowed in Machine-mode. 

For this reason, the implementation of PMP was extended using two unused Bits in the configuration registers that encode for which mode the privileges are applied.

Protecting memory follows an opt-in, opt-out principe:
\begin{itemize}
 \item Everything which is not explicitely allowed in User-mode is forbidden
 \item Everything which is not explicitely forbidden in Machine-mode is allowed
\end{itemize}

This only applies if there is at least one active protection rule.



\subsubsection{Protected Memory Areas}


The following describes how the memory was protected in the reference firmware for the IOTA Crypto Core module.

\begin{center}
\includegraphics[width=12cm]{img/pmp.pdf}
\end{center}

The characters R, W, X in the diagram above encode the privileges `readable`, `writable` and `executable`. 


In Machine-mode the entire ROM is executable with exception of a small section used for privileged data - the rest of the memory is neither in Machine- nor in User-mode executable. There is an extra stack for privileged code that is protected against overflows by an `overflow-protection` (a single 32bit address which is not accessible in Machine- or User-mode) which prevents leaking of sensitive data into user-RAM (either intended by trying to exploit stack-overflows or unintended by using to much stack for local variables in privileged code). This single 32bit address is completly locked for Machine- and User-mode and access causes the system to halt completly. Additionally, there is a 4kB memory block in which AES- and API-keys are stored. It also is used as stack for the trap-handler. 

In User-mode, only access to memory for user-Code and user-RAM or peripherals such as GPIOs, I2C, SPI and UART peripherals is allowed. 

One further speciality is that protection for privileged code and data is dynamically initialized at the start of the firmware - so no static memory protection layout is needed which allows to use the rest that is not used by privileged code or data for user code and data.

Though, there is one detail the developer should be aware of: Privileged code and data has to be explicitely assigned to protected memory sections in the source-code.

This is done by simply adding `MMTEXT` (readable, executable, not writable), `MMDATA` (readable, writeable, not executable) or `MMRODATA` (readable, not writable, not executable) to functions or variables.


\begin{lstlisting}[language=c]
uint32_t MMDATA protected_variable;

const uint32_t MMRODATA protected_constant = 0xc001cafe;

uint32_t MMTEXT protected_function() {
...
}
\end{lstlisting}






\subsubsection{Calls to Privileged Code}

Most peripherals are protected by PMP, preventing User-code to use it directly. Instead, the code in User-mode has to call a binary interface (supervisor binary interface) which executes privileged code.

The program flow is as following:

\begin{itemize}
 \item User-code calls a user-callable function in the SBI-namespace
 \begin{lstlisting}[language=c]
StatusResponse* reboot(uint32_t address) {
	return (StatusResponse*) SBI_CALL_1(REBOOT, address);
}
 \end{lstlisting}

 \item The function in the SBI-namespace executes an `ECALL` (jump to the trap-handler that is running in machine-mode) 
\begin{lstlisting}[language=c]
#define SBI_CALL(func, arg0, arg1, arg2, arg3, arg4) ({         \
    register uintptr_t a0 asm ("a0") = (uintptr_t)(func);       \
    register uintptr_t a1 asm ("a1") = (uintptr_t)(arg0);       \
    register uintptr_t a2 asm ("a2") = (uintptr_t)(arg1);       \
    register uintptr_t a3 asm ("a3") = (uintptr_t)(arg2);       \
    register uintptr_t a4 asm ("a4") = (uintptr_t)(arg3);       \
    register uintptr_t a5 asm ("a5") = (uintptr_t)(arg4);       \
    asm volatile ("ecall"                                       \
          : "+r" (a0)                                           \
          : "r" (a1), "r" (a2), "r" (a3), "r" (a4), "r" (a5)    \
          : "memory");                                          \
    a0;                                                         \
})

#define SBI_CALL_1(which, arg0) \
        SBI_CALL(which, arg0, 0, 0, 0, 0)
\end{lstlisting}
 
 \item The trap-handler returns in machine-mode to the dispatcher-function that evaluates the `func`-parameter and calls the right function
  \begin{lstlisting}[language=c]
uint32_t* MMTEXT sbiCall(
    uint32_t func, uint32_t arg0, uint32_t arg1, 
    uint32_t arg2, uint32_t arg3, uint32_t arg4) {
    
    switch (func) {
// ...
    case REBOOT:
        return (uint32_t*) _reboot(arg0);
// ...
    return 0;
} \end{lstlisting}

\item Following an example of the privileged `reboot` command:
\begin{lstlisting}[language=c]
StatusResponse* _reboot(uint32_t address) {
    StatusResponse* resp = &statusResponse;
    do {
        if ((address & 0x0000ffff) || address > 0x1000000) {
            resp->error(SBIError::INVALID_ADDRESS);
            break;
        }
        // iprog is triggered one second after
        SecureGPIO::triggerIPROG(address);
        resp->error(SBIError::NONE);
    } while(0);
    return resp;
} 
\end{lstlisting}


 \item After executing the privileged function, the CPU returns to the user-callable function, returning a pointer to an object. This object could
 contain only a status code or data or pointers to data. The return-objects are statically defined and are reused with each call for safety reasons.
 
\end{itemize}








